package craky.component;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

import craky.util.WindowMove;

public class JLoadingDialog extends JDialog
  implements ActionListener
{
  private static final long serialVersionUID = -6555677237581881143L;
  private Container contentPane;
  private JProgressBar progressBar;
  private JLabel lbInfo;
  private JButton btnCancel;
  private Window parent;
  private ComponentListener listener;
  private Runnable command;
  private String statusInfo;
  private boolean cancelButtonVisible;
  private boolean cancelled;
  private Action finishedAction;
  private Action cancelAction;
  private ScheduledThreadPoolExecutor executor;
  private ScheduledFuture<?> future;
  private WindowMove move;

  public JLoadingDialog(Window parent)
  {
    this(parent, null, null);
  }

  public JLoadingDialog(Window parent, Runnable command, String statusInfo)
  {
    this(parent, command, statusInfo, true);
  }

  public JLoadingDialog(Window parent, Runnable command, String statusInfo, boolean cancelButtonVisible)
  {
    super(parent, ModalityType.DOCUMENT_MODAL);
    this.move = new WindowMove(this);
    this.parent = parent;
    this.command = command;
    this.statusInfo = statusInfo;
    this.cancelButtonVisible = cancelButtonVisible;
    initUI();
  }

  private void initUI()
  {
    this.contentPane = getContentPane();
    this.progressBar = new JRound3DProgressBar();
    this.lbInfo = new JLabel("<html>" + this.statusInfo);
    this.btnCancel = new JButton(UIManager.getString("OptionPane.cancelButtonText"));
    this.listener = new ComponentAdapter()
    {
      public void componentResized(ComponentEvent e)
      {
        JLoadingDialog.this.reLayout();
      }
    };
    setDefaultCloseOperation(0);
    this.progressBar.setPreferredSize(new Dimension(-1, 15));
    this.progressBar.setIndeterminate(true);
    this.btnCancel.setPreferredSize(new Dimension(75, 22));
    this.btnCancel.addActionListener(this);
    this.btnCancel.setVisible(this.cancelButtonVisible);
    this.lbInfo.setVerticalAlignment(1);
    this.contentPane.setLayout(null);
    this.contentPane.add(this.progressBar);
    this.contentPane.add(this.lbInfo);
    this.contentPane.add(this.btnCancel);
    this.contentPane.addComponentListener(this.listener);
    setUndecorated(true);
    setResizable(false);
    setBorder(new LineBorder(new Color(200, 230, 230), 2, false));
    setSize(390, 100);
    setLocationRelativeTo(this.parent);

    addWindowListener(new WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        JLoadingDialog.this.actionPerformed(null);
      }
    });
  }

  private void reLayout() {
    int width = this.contentPane.getWidth();
    int height = this.contentPane.getHeight();
    Dimension buttonSize = this.btnCancel.getPreferredSize();
    Dimension barSize = this.progressBar.getPreferredSize();
    int labelY = 30 + barSize.height;
    this.progressBar.setBounds(15, 20, width - 30, barSize.height);
    this.lbInfo.setBounds(15, labelY, width - 30, height - 10 - labelY);

    if (this.btnCancel.isVisible())
    {
      this.btnCancel.setBounds(width - buttonSize.width - 10, height - buttonSize.height - 10, buttonSize.width, buttonSize.height);
    }
  }

  public void changeContentPane(Container newContentPane)
  {
    this.contentPane.removeComponentListener(this.listener);
    newContentPane.removeAll();
    newContentPane.setLayout(null);
    newContentPane.addComponentListener(this.listener);

    for (Component c : this.contentPane.getComponents())
    {
      newContentPane.add(c);
    }

    setContentPane(newContentPane);
    getRootPane().doLayout();
    this.contentPane = newContentPane;
  }

  public void runCommand(Runnable command)
  {
    this.command = command;
    runCommand();
  }

  public void runCommand()
  {
    this.cancelled = false;
    Task task = new Task(this.command, null);
    this.executor = new ScheduledThreadPoolExecutor(1);
    this.future = this.executor.schedule(task, 0L, TimeUnit.MILLISECONDS);
    setVisible(true);
  }

  public void stopCommand()
  {
    this.future.cancel(true);
    this.cancelled = true;
  }

  public void actionPerformed(ActionEvent e)
  {
    if (this.cancelButtonVisible)
    {
      if (this.cancelAction != null)
      {
        this.cancelAction.actionPerformed(null);
      }
      else
      {
        stopCommand();
      }
    }
  }

  public void setBorder(Border border)
  {
    getRootPane().setBorder(border);
  }

  public Border getBorder()
  {
    return getRootPane().getBorder();
  }

  public JProgressBar getProgressBar()
  {
    return this.progressBar;
  }

  public JLabel getInfoLabel()
  {
    return this.lbInfo;
  }

  public JButton getCancelButton()
  {
    return this.btnCancel;
  }

  public Window getParent()
  {
    return this.parent;
  }

  public Runnable getCommand()
  {
    return this.command;
  }

  public String getStatusInfo()
  {
    return this.statusInfo;
  }

  public void setStatusInfo(String statusInfo)
  {
    this.statusInfo = statusInfo;
    this.lbInfo.setText("<html>" + statusInfo);
  }

  public ActionListener getCancelAction()
  {
    return this.cancelAction;
  }

  public void setCancelAction(Action cancelAction)
  {
    this.cancelAction = cancelAction;
  }

  public Action getFinishedAction()
  {
    return this.finishedAction;
  }

  public void setFinishedAction(Action finishedAction)
  {
    this.finishedAction = finishedAction;
  }

  public boolean isCancelButtonVisible()
  {
    return this.cancelButtonVisible;
  }

  public void setCancelButtonVisible(boolean cancelButtonVisible)
  {
    this.cancelButtonVisible = cancelButtonVisible;
    this.btnCancel.setVisible(cancelButtonVisible);
  }

  public boolean isMoveable()
  {
    return this.move.isMoveable();
  }

  public void setMoveable(boolean moveable)
  {
    this.move.setMoveable(moveable);
  }

  public boolean isCancelled()
  {
    return this.cancelled;
  }

  private class Task<V> extends FutureTask<V>
  {
	  public Task(Runnable runnable,V result)
    {
      super(runnable, result);
    }

    protected void done()
    {
      JLoadingDialog.this.executor.shutdown();
      JLoadingDialog.this.executor = null;
      JLoadingDialog.this.future = null;
      JLoadingDialog.this.dispose();

      if ((!JLoadingDialog.this.cancelled) && (JLoadingDialog.this.finishedAction != null))
      {
        JLoadingDialog.this.finishedAction.actionPerformed(null);
      }
    }
  }
}